﻿using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestByWire.Test.Fakes;

namespace TestByWire.Test
{
	[TestClass]
	public class LambdaExpressionComparerTests
	{
		private LambdaExpressionComparer _target;

		[TestInitialize]
		public void Initialize()
		{
			_target=new LambdaExpressionComparer();
		}

		[TestMethod]
		public void NoParameters()
		{
			Expression<Func<ICalculate, int>> cal = x => x.Calc();
			Expression<Func<ICalculate, int>> cal2 = x => x.Calc();
			Expression<Func<ICalculate, int>> cal3 = x => x.Calc(3);

			Assert.AreEqual(0, _target.Compare(cal, cal2));
			Assert.AreNotEqual(0, _target.Compare(cal, cal3));
		}

		[TestMethod]
		public void NoParametersAction()
		{
			Expression<Action<ICalculate>> cal = x => x.SetValue();
			Expression<Action<ICalculate>> cal2 = x => x.SetValue();
			Expression<Action<ICalculate>> cal3 = x => x.SetValue(3);

			Assert.AreEqual(0, _target.Compare(cal, cal2));
			Assert.AreNotEqual(0, _target.Compare(cal, cal3));
		}

		[TestMethod]
		public void OneParameter()
		{
			Expression<Func<ICalculate, int>> cal = x => x.Calc(2);
			Expression<Func<ICalculate, int>> cal2 = x => x.Calc(2);
			Expression<Func<ICalculate, int>> cal3 = x => x.Calc(3);

			Assert.AreEqual(0, _target.Compare(cal, cal2));
			Assert.AreEqual(0, _target.Compare(cal, cal3));
		}

		[TestMethod]
		public void OneParameterAction()
		{
			Expression<Action<ICalculate>> cal = x => x.SetValue(2);
			Expression<Action<ICalculate>> cal2 = x => x.SetValue(2);
			Expression<Action<ICalculate>> cal3 = x => x.SetValue(3);

			Assert.AreEqual(0, _target.Compare(cal, cal2));
			Assert.AreEqual(0, _target.Compare(cal, cal3));
		}

		[TestMethod]
		public void OneParameterDifferentArgumentTypes()
		{
			Expression<Func<ICalculate, int>> cal = x => x.Calc(2);
			Expression<Func<ICalculate, int>> cal2 = x => x.Calc((double)2);

			Assert.AreNotEqual(0, _target.Compare(cal, cal2));
		}

		[TestMethod]
		public void SameSignatureDifferentTypes()
		{
			Expression<Func<ICalculate, int>> cal = x => x.Calc(2);
			Expression<Func<ICalculate2, int>> cal2 = x => x.Calc(2);

			Assert.AreNotEqual(0, _target.Compare(cal, cal2));
		}

		[TestMethod]
		public void TwoParameters()
		{
			Expression<Func<ICalculate, int>> cal = x => x.Calc(2, 2);
			Expression<Func<ICalculate, int>> cal2 = x => x.Calc(2, 2);
			Expression<Func<ICalculate, int>> cal3 = x => x.Calc(2, 3);

			Assert.AreEqual(0, _target.Compare(cal, cal2));
			Assert.AreEqual(0, _target.Compare(cal, cal3));
		}

        [TestMethod]
        public void SameSignature()
        {
            Expression<Func<ICalculate, int>> cal = x => x.Calc(2, 2);
            Expression<Func<ICalculate, int>> cal2 = x => x.Calc2(2, 2);

            Assert.AreNotEqual(0, _target.Compare(cal, cal2));
        }

		[TestMethod]
		public void Compare_MustReturn0_IfXandYAreNull()
		{
			int result = _target.Compare(default(object), default(object));

			Assert.AreEqual(0, result);
		}

		[TestMethod]
		public void Compare_MustReturn1_IfXisNullAndYIsNot()
		{
			int result = _target.Compare(default(object), new object());

			Assert.AreEqual(1, result);
		}

		[TestMethod]
		public void Compare_MustReturnMinus1_IfYisNullAndXisNot()
		{
			int result = _target.Compare(new object(), default(object));

			Assert.AreEqual(-1, result);
		}

		[TestMethod]
		public void Compare_MustCompareTypeNames_IfXandYareNotLambdaExpressions()
		{
			int result = _target.Compare("10", "20");

			Assert.AreEqual(0, result);
		}

		private bool Eq(LambdaExpression action, LambdaExpression action2)
		{
			if (action.Parameters.Count != action2.Parameters.Count)
				return false;

			for (int i = 0; i < action.Parameters.Count; i++)
			{
				if (action.Parameters[i].Type != action2.Parameters[i].Type)
					return false;
			}

			var col1 = ((MethodCallExpression)action.Body).Arguments;
			var col2 = ((MethodCallExpression)action2.Body).Arguments;

			if (col1.Count != col2.Count())
				return false;

			for (int i = 0; i < col1.Count; i++)
			{
				var arg1 = (ConstantExpression)col1[i];
				var arg2 = (ConstantExpression)col2[i];
				if (!Equals(arg1.Value, arg2.Value))
					return false;
			}
			return true;
		}

	}
}
